package com.knowswift.security.provider;

import com.knowswift.common.bean.system.po.LoginLog;
import com.knowswift.common.bean.wechat.dto.WechatResponse;
import com.knowswift.common.utils.JSONUtils;
import com.knowswift.security.AuthUserDetails;
import com.knowswift.security.AuthUserDetailsService;
import com.knowswift.security.log.LoginLogService;
import com.knowswift.security.param.MiniLoginParam;
import com.knowswift.security.token.MiniProgramAuthenticationToken;
import com.knowswift.security.util.wechat.MiniProgramLoginUtil;
import lombok.SneakyThrows;
import org.apache.commons.lang3.StringUtils;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;

import java.time.LocalDateTime;
import java.util.Collection;

/**
 * @company https://www.knowswift.com
 */
public class MiniProgramAuthenticationProvider extends AbstractMatchClassAuthenticationProvider {

    private final LoginLogService loginLogService;

    public MiniProgramAuthenticationProvider(AuthUserDetailsService userDetailsService, Class<? extends MiniProgramAuthenticationToken> matchClass, LoginLogService loginLogService) {
        super(userDetailsService, matchClass);
        this.loginLogService = loginLogService;
    }

    @SneakyThrows
    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        MiniProgramAuthenticationToken miniProgramAuthenticationToken = (MiniProgramAuthenticationToken) authentication;

        MiniLoginParam miniLoginParam = miniProgramAuthenticationToken.getMiniLoginParam();

        WechatResponse wechatResponse = MiniProgramLoginUtil.userMiniProgramAuthorization(miniLoginParam.getCode());

        if (wechatResponse == null || wechatResponse.getErrorCode() != null) {
            saveLog(miniProgramAuthenticationToken, wechatResponse);
            throw new BadCredentialsException("请求异常");
        }
        String openId = wechatResponse.getOpenId();
        String unionId = wechatResponse.getUnionId();
        String sessionKey = wechatResponse.getSessionKey();
        WechatResponse decrypt = null;
        try {
            // 解密手机号码
            decrypt = MiniProgramLoginUtil.decrypt(miniLoginParam.getEncryptedData(), miniLoginParam.getIv(), sessionKey);
            if (decrypt == null || StringUtils.isBlank(decrypt.getPurePhoneNumber())) {
                saveLog(miniProgramAuthenticationToken, decrypt);
                throw new BadCredentialsException("登录失败，请重试");
            }
            AuthUserDetails authUserDetails = userDetailsService.loadUserByMiniOpenId(unionId, openId, decrypt);
            checkAuthUserDetails(authUserDetails);
            return matchClass.getConstructor(Collection.class, AuthUserDetails.class, MiniLoginParam.class)
                    .newInstance(authUserDetails.getAuthorities(), authUserDetails, miniLoginParam);
        } catch (Exception e) {
            saveLog(miniProgramAuthenticationToken, decrypt);
            throw new BadCredentialsException("登录失败，请重试");
        }
    }

    private void saveLog(MiniProgramAuthenticationToken miniProgramAuthenticationToken, WechatResponse wechatResponse) {
        LoginLog systemLog = new LoginLog();
        systemLog.setLoginResult("授权异常");
        systemLog.setException(JSONUtils.toJSONString(wechatResponse));
        if (wechatResponse != null) {
            systemLog.setException(wechatResponse.getErrormsg());
        }
        systemLog.setCreateTime(LocalDateTime.now());
        systemLog.setParam(JSONUtils.toJSONString(miniProgramAuthenticationToken));
        systemLog.setLogPoint("user/index/miniProgram/Provider");
        loginLogService.save(systemLog);
    }
}
